﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;

namespace Barbarian.Win
{
    class PlayerAnimator
    {

        #region Private variables

        private Texture2D _texture;
        private int _skins;
        private int _frameHeight;
        private int _frameWidth;
        private AnimationName _currAnim = AnimationName.Idle;
        private double _currFrame = 0;
        private int _currSkin = 0;
        private InputSource _inpSrc;

        #endregion

        public enum InputSource
        {
            Keyboard = 0,
            Mouse = 1,
            Joystick = 2,
            CPU_Easy = 3,
            CPU_Medium = 4,
            CPU_Hard = 5
        }

        #region Animation Detail

        class AnimationData
        {
            public bool inhibitUserInput;
            public double framesRunLength;              // in Milliseconds
            public int[] frameSequence;
            public AnimationName joinToAnimation;       // What animation to play when this finishes (use same to loop)

            public AnimationData(bool stopInput, float animSpeed, int[] animSequence, AnimationName nextAnim)
            {
                inhibitUserInput = stopInput;
                framesRunLength = animSpeed;
                frameSequence = animSequence;
                joinToAnimation = nextAnim;
            }
        }

        public enum AnimationName : int
        {
            Idle = 0,
            WalkForward = 1,
            WalkBackward = 2,
            Jump = 3
        }

        private AnimationData[] playerAnims = new AnimationData[] {
      new AnimationData( false, 100, new int[] { 0 }, AnimationName.Idle ),                 // Idle animation (1 frame)
      new AnimationData( false, 100, new int[] { 0, 1, 2, 3 }, AnimationName.WalkForward ),        // Walk forward animation (4 frames)
      new AnimationData( false, 100, new int[] { 0, 3, 2, 1 }, AnimationName.WalkBackward ),        // Walk backwards animation (4 frames)
      new AnimationData( true, 100, new int[] { 0, 8, 9, 9, 8 }, AnimationName.Idle )      // Jump animation (5 frames)
    };

        #endregion

        // Skins should all have the same frames and be stored row after row in the same image file.
        public PlayerAnimator(Texture2D spriteSheet, int numberOfSkins, int frameWidth)
        {
            _texture = spriteSheet;
            _skins = numberOfSkins;
            _frameHeight = _texture.Height / _skins;
            _frameWidth = frameWidth;
        }

        // Call this at the start before using the character
        public void SetupPlayer(AnimationName startingAnimation, InputSource controlMethod, int characterSkin)
        {
            _inpSrc = controlMethod;
            Skin = characterSkin;
            StartAnimation(startingAnimation);
        }

        // Change the character's animation to be played (and resets frame count)
        public void StartAnimation(AnimationName animName)
        {
            _currAnim = animName;
            _currFrame = 0;
        }

        // Which skin to use from the sprite sheet
        public int Skin
        {
            get
            {
                return _currSkin;
            }
            set
            {
                if (value < _skins && value >= 0)
                    _currSkin = value;
            }
        }

        // Call this each Update call
        public void HandleInput()
        {
            if (playerAnims[(int)_currAnim].inhibitUserInput)
                return;

            // TODO: Process input
            switch (_inpSrc)
            {
                case InputSource.Keyboard:
                    KeyboardState keyboardState = Keyboard.GetState();
                    if (keyboardState.IsKeyDown(Keys.Right))
                    {
                        if (_currAnim != AnimationName.WalkForward)
                            StartAnimation(AnimationName.WalkForward);
                    }
                    else if (keyboardState.IsKeyDown(Keys.Left))
                    {
                        if (_currAnim != AnimationName.WalkBackward)
                            StartAnimation(AnimationName.WalkBackward);
                    }
                    else if (keyboardState.IsKeyDown(Keys.Up))
                        StartAnimation(AnimationName.Jump);
                    else
                        StartAnimation(AnimationName.Idle);
                    break;
                case InputSource.Mouse:
                    break;
                case InputSource.Joystick:
                    break;
                case InputSource.CPU_Easy:
                    break;
                case InputSource.CPU_Medium:
                    break;
                case InputSource.CPU_Hard:
                    break;
            }
        }

        public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Vector2 position, SpriteEffects spriteEffects)
        {
            double framesToAdd;

            // Calculate frames to add based upon milliseconds
            framesToAdd = (gameTime.ElapsedGameTime.TotalMilliseconds / playerAnims[(int)_currAnim].framesRunLength);

            // Add the new frames count, but loop at the number of frames in the animation
            _currFrame += framesToAdd;
            if (_currFrame >= playerAnims[(int)_currAnim].frameSequence.Length)
            {
                if (_currAnim == playerAnims[(int)_currAnim].joinToAnimation)
                    _currFrame = _currFrame % playerAnims[(int)_currAnim].frameSequence.Length;
                else
                    StartAnimation(playerAnims[(int)_currAnim].joinToAnimation);      // We're on a fire once anim, move onto the next anim
            }

            // Calculate source rectangle to draw
            Rectangle source = new Rectangle(playerAnims[(int)_currAnim].frameSequence[(int)_currFrame] * _frameWidth, _currSkin * _frameHeight, _frameWidth, _frameHeight);

            // Draw the current frame.
            spriteBatch.Draw(_texture, position, source, Color.White, 0.0f, new Vector2(0, 0), 1.0f, spriteEffects, 0.0f);
        }
    }
}